package com.mioto.ld.communication.mqtt.protocol;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.mioto.ld.communication.DeviceNotifyProcess;
import com.mioto.ld.communication.mqtt.MqttHelper;
import com.mioto.ld.communication.mqtt.protocol.cache.DeviceInfoCache;
import com.mioto.ld.communication.mqtt.protocol.cache.ReportAttributeCache;
import com.mioto.ld.communication.mqtt.protocol.model.Message;
import com.mioto.ld.communication.mqtt.protocol.model.device.DeviceDataReportPayload;
import com.mioto.ld.communication.mqtt.protocol.model.device.DeviceFaultReportPayload;
import com.mioto.ld.communication.mqtt.protocol.model.server.DeiveClosePayload;
import com.mioto.ld.component.RelationOperationEnum;
import com.mioto.ld.module.alert.model.DeviceTriggerConditionDTO;
import com.mioto.ld.module.alert.model.LinkageActionType;
import com.mioto.ld.module.alert.model.LinkageFunctionPO;
import com.mioto.ld.module.alert.service.IAlertTriggerService;
import com.mioto.ld.module.alert.service.ILinkageFunctionService;
import com.mioto.ld.module.alert.service.ILinkageTriggerService;
import com.mioto.ld.module.device.model.Device;
import com.mioto.ld.module.device.model.DeviceAlertLog;
import com.mioto.ld.module.device.model.DeviceFaultResolveStatus;
import com.mioto.ld.module.device.service.IDeviceAlertLogService;
import com.mioto.ld.module.product.service.IProductAttributeService;
import com.mioto.ld.utils.CompareUtil;
import com.mioto.ld.utils.SpringBeanUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author mioto-qinxj
 * @date 2024/6/21
 * @desc
 */
@Slf4j
public abstract class TriggerHandler {

    private final int intervalMinute = 4;
    protected IAlertTriggerService alertTriggerService;
    protected DeviceInfoCache deviceInfoCache;
    protected IDeviceAlertLogService deviceAlertLogService;

    protected DeviceNotifyProcess deviceNotifyProcess;
    protected ILinkageTriggerService linkageTriggerService;
    TriggerHandler(){
        alertTriggerService = SpringBeanUtil.getBean(IAlertTriggerService.class);
        deviceInfoCache = SpringBeanUtil.getBean(DeviceInfoCache.class);
        deviceAlertLogService = SpringBeanUtil.getBean(IDeviceAlertLogService.class);
        deviceNotifyProcess = SpringBeanUtil.getBean(DeviceNotifyProcess.class);
        linkageTriggerService = SpringBeanUtil.getBean(ILinkageTriggerService.class);
    }
    @Transactional(rollbackFor = Exception.class)
    public void handle(Message message){
        Device device = deviceInfoCache.get(message.getSn());
        if (ObjectUtil.isNotNull(device)) {
            triggerProcess(message, device);
        }
    }

    /**
     * 执行联动
     * @param device
     * @param linkageId
     */
    protected void executeLinkageFunction(Device device,int linkageId){
        log.info("linkage trigger meet the conditions,execute linkage functions");
        List<LinkageFunctionPO> list = SpringBeanUtil.getBean(ILinkageFunctionService.class).findLinkageFunctionPOsByLinkageId(linkageId);
        if (CollUtil.isNotEmpty(list)){
            for (LinkageFunctionPO linkageFunctionPO : list) {
                if (ObjectUtil.equal(linkageFunctionPO.getFunctionType(), LinkageActionType.LINKAGE_ACTION_TYPE_FUNCTION)){
                    log.info("execute device function,send message to device - {}",device.getSn());
                    MqttHelper.control(device.getSn(),new DeiveClosePayload());
                }else if (ObjectUtil.equal(linkageFunctionPO.getFunctionType(),LinkageActionType.LINKAGE_ACTION_TYPE_NOTIFY)){
                    deviceNotifyProcess.executeMessageNotify(linkageFunctionPO,device);
                }
            }
        }
    }


    /**
     * 保存告警记录
     * @param deviceId
     * @param alertId
     * @param reportTime
     * @param triggerCondition
     * @return
     */
    protected boolean saveAlertLog(int deviceId, int alertId, Date reportTime, String triggerCondition){
        //如果一定时间内重复触发同一种告警，则只保存一条记录
        DeviceAlertLog lastLevelAlertLog = deviceAlertLogService.findLastLevelAlert(alertId,deviceId);
        if (ObjectUtil.isNull(lastLevelAlertLog)) { // || DateUtil.between(lastLevelAlertLog.getAlertDate(),reportTime, DateUnit.MINUTE) > intervalMinute
            log.info("alert trigger meet the conditions,save alert log in database");
            DeviceAlertLog deviceAlertLog = new DeviceAlertLog();
            deviceAlertLog.setStatus(DeviceFaultResolveStatus.DEVICE_FAULT_RESOLVE_UNDISPOSED);
            deviceAlertLog.setDeviceId(deviceId);
            deviceAlertLog.setAlertDate(reportTime);
            deviceAlertLog.setAlertId(alertId);
            deviceAlertLog.setTriggerCondition(triggerCondition);
            deviceAlertLogService.insert(deviceAlertLog);
            return true;
        }
        return false;
    }

    /**
     * 条件比对，是否满足触发器条件
     * @param condition 触发器条件
     * @param targetId  触发属性id
     * @param customAttr 系统自定义属性集合
     * @param reportAttr 设备上报属性集合
     * @return true - 符合条件 false - 不符合
     */
    protected Boolean checkAttributeTriggerConditions(String condition,int targetId,Map<Integer,String> customAttr,Map<String,Object> reportAttr){
        JSONArray jsonArray = JSONUtil.parseArray(condition);
        String attrName = customAttr.get(targetId);
        boolean compareResult = false;
        if (StrUtil.isEmpty(attrName)){
            log.warn("customAttr - {},targetId - {},reportAttr - {}",JSONUtil.parse(customAttr),targetId,JSONUtil.parse(reportAttr));
        }else {
            DeviceTriggerConditionDTO triggerConditionDTO;
            log.info("report attribute - {} : {}", attrName, reportAttr.get(attrName));
            for (Object o : jsonArray) {
                triggerConditionDTO = JSONUtil.parseObj(o).toBean(DeviceTriggerConditionDTO.class);
                log.info("attribute {} trigger condition - value {} {}", attrName,
                        RelationOperationEnum.getInstance(triggerConditionDTO.getCondition()).getOperator(), triggerConditionDTO.getTargetValue());
                compareResult = CompareUtil.OPERATION_MAP.get(RelationOperationEnum.getInstance(triggerConditionDTO.getCondition()))
                        .calculate(Integer.parseInt(ObjectUtil.toString(reportAttr.get(attrName))), triggerConditionDTO.getTargetValue());
                log.info("compare result - {}", compareResult);
                if (!compareResult) {
                    break;
                }
            }
        }
        return compareResult;
    }


    /**
     * 根据上报的属性匹配系统中的属性
     * @param productId
     * @param reportAttr
     * @return
     */
    protected Map<Integer,String> findDeviceAttributes(int productId, Map<String,Object> reportAttr){
        if (CollUtil.isNotEmpty(reportAttr)) {
            String[] attrNames = reportAttr.keySet().toArray(new String[reportAttr.size()]);
            return SpringBeanUtil.getBean(IProductAttributeService.class).findIdAndIdentityByProductIdAndAttr(productId, attrNames);
        }
        return null;
    }


    /**
     * 触发器处理
     * @param message
     * @param device
     */
    protected abstract void triggerProcess(Message message,Device device);
}
